package com.patui.Weibo;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Random;

import com.patui.Utils.PLog;

import android.os.Bundle;
import android.util.Log;
public abstract class HttpHeaderFactory {
	public final static String TAG="@___Patui.HttpHeaderFactory";
	public final static String SPLITTER="___";
	
	public static final String CONST_HMAC_SHA1 = "HmacSHA1";
	public static final String CONST_SIGNATURE_METHOD = "HMAC-SHA1";
	public static final String CONST_OAUTH_VERSION = "1.0";
	
	public HttpHeaderFactory()
	{
	}

	public String getWeiboAuthHeader(String method, String url, WeiboParameters postParam,
			String app_key, String app_secret, Token token) throws WeiboException {
		//step 1: 生存时间戳和单次值
		final long timestamp = System.currentTimeMillis() / 1000;
        final long nonce = timestamp + (new Random()).nextInt();
        
        //step 2: 生成基本baseOauthParams
        WeiboParameters baseOauthParams = this.generateBaseAuthParameters(nonce, timestamp, token);
        
        //由baseOauthParams生成signatureOauthParams
        WeiboParameters signatureOauthParams = this.generateSignatureParameters(baseOauthParams, postParam, url);
        
        //step 3:有signatureOauthParams生成用于签名的base String
        String oauthBaseString = this.generateAuthSignature(method, signatureOauthParams, url, token);
        
        //step 4: 生成oauth_signature_str
        //调用继承类RequestHeader或是RequestTokenHeader或是AccessTokenHeader的响应generateSignature方法
        String signatureStr = generateSignature(oauthBaseString, token);
        
        //这里应该是OauthParams为了方便上面的清楚显示
        baseOauthParams.add("oauth_signature", signatureStr);
        
        //step 5: for additional parameters
        this.addAdditionalParams(baseOauthParams, postParam);
        return "OAuth " + encodeParametersToString(baseOauthParams, ",", true);
	}
	
	//Step 1.构造源串:HTTP请求方式 & urlencode(uri) & urlencode(a=x&b=y&...)
	private String generateAuthSignature(final String method, WeiboParameters signatureParams, 
			final String url, Token token)
	{
		StringBuffer base = new StringBuffer(method).append("&").append(encode(constructRequestURL(url)))
        .append("&");
        base.append(encode(encodeParametersToString(signatureParams, "&", false)));
        //将字符串变量转换成字符常量
        String oauthSignatureString = base.toString();
        //Log.i(TAG, SPLITTER+"oauthSignatureString="+oauthSignatureString);
		return oauthSignatureString;
	}
	
	
	private WeiboParameters generateSignatureParameters(WeiboParameters baseOauthParams, WeiboParameters postParam, String url) 
	throws WeiboException{
		WeiboParameters signatureParams = new WeiboParameters();
        signatureParams.addAll(baseOauthParams);
        signatureParams.add("source", Weibo.APP_KEY);
        signatureParams.addAll(postParam);
        this.parseUrlParameters(url, signatureParams);
        //给Parameters排序
        WeiboParameters  signatureParameters= generateSignatureSort(signatureParams);
        return signatureParameters;
	}
	
	
	private WeiboParameters generateBaseAuthParameters(long nonce, long timestamp, Token token){
		WeiboParameters authParams = new WeiboParameters();
        authParams.add("oauth_consumer_key", Weibo.APP_KEY);
        authParams.add("oauth_nonce", String.valueOf(nonce));
        authParams.add("oauth_signature_method", HttpHeaderFactory.CONST_SIGNATURE_METHOD);
        authParams.add("oauth_timestamp", String.valueOf(timestamp));
        authParams.add("oauth_version", HttpHeaderFactory.CONST_OAUTH_VERSION);
        if(token != null)
        {
        	authParams.add("oauth_token", token.getToken());
        }
        else
        {
        	authParams.add("source", Weibo.APP_KEY);
        } 
        return authParams;
	}
	
	//生成用于哈希的base string串，注意要按顺序，按需文档需求参数生成，否则40107错误
	public abstract WeiboParameters generateSignatureSort(WeiboParameters bundle);
	
	//add additional parameters to des key-value pairs,support to expanding params 
	public abstract void addAdditionalParams(WeiboParameters des, WeiboParameters src);
	
	//解析url中参数对,存储到signatureBaseParams
	public void parseUrlParameters(String url, WeiboParameters signatureBaseParams) throws WeiboException {
		//Log.i(TAG, "__parseUrlParameters:"+url);
		int queryStart = url.indexOf("?");
        if (-1 != queryStart) 
        {
            String[] queryStrs = url.substring(queryStart + 1).split("&");
            try {
                for (String query : queryStrs) 
                {
                    String[] split = query.split("=");
                    if (split.length == 2) 
                    {
                        signatureBaseParams.add(
                                URLDecoder.decode(split[0],
                                        "UTF-8"), URLDecoder.decode(split[1],
                                        "UTF-8"));
                    } 
                    else 
                    {
                        signatureBaseParams.add( URLDecoder.decode(split[0],
                                        "UTF-8"), "");
                    }
                }
            } catch (UnsupportedEncodingException e) {
            	throw new WeiboException(e);
            }

        }

	}

	
	public abstract String generateSignature(String data, Token token) throws WeiboException;
	
	
	public static String encodeParametersToString(WeiboParameters postParams, String splitter, boolean quot) {
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < postParams.size(); i++) 
        {
            if (buf.length() != 0) 
            {
                if (quot) 
                {
                    buf.append("\"");
                }
                buf.append(splitter);
            }
            buf.append(encode(postParams.getKey(i))).append("=");
            if (quot) 
            {
                buf.append("\"");
            }
            buf.append(
                encode(postParams.getValue(i)));
        }
        if (buf.length() != 0) {
            if (quot) {
                buf.append("\"");
            }
        }
        return buf.toString();
    }
	
	
	public static String encodeParameters(Bundle postParams, String split, boolean quot) {
		final String splitter = split;
        StringBuffer buf = new StringBuffer();
        for (String key : postParams.keySet()) {
            if (buf.length() != 0) {
                if (quot) {
                    buf.append("\"");
                }
                buf.append(splitter);
            }
            buf.append(encode(key)).append("=");
            if (quot) {
                buf.append("\"");
            }
            buf.append(
                    encode(postParams.getString(key)));
        }
        if (buf.length() != 0) {
            if (quot) {
                buf.append("\"");
            }
        }
        return buf.toString();
    }
	
	
	//
	public static String constructRequestURL(String url) {
		//Log.i(TAG, "__constructRequestURL url start:"+url);
        int index = url.indexOf("?");
        if (-1 != index) 
        {
            url = url.substring(0, index);
        }
        int slashIndex = url.indexOf("/", 8);
        String baseURL = url.substring(0, slashIndex).toLowerCase();
        int colonIndex = baseURL.indexOf(":", 8);
        if (-1 != colonIndex) 
        {
            // url contains port number
            if (baseURL.startsWith("http://") && baseURL.endsWith(":80")) {
                // http default port 80 MUST be excluded
                baseURL = baseURL.substring(0, colonIndex);
            } 
            else if (baseURL.startsWith("https://") && baseURL.endsWith(":443"))
            {
                // http default port 443 MUST be excluded
                baseURL = baseURL.substring(0, colonIndex);
            }
        }
        url = baseURL + url.substring(slashIndex);
        //Log.i(TAG, "__constructRequestURL url end:"+url);
        return url;
    }
	
	
	/**
     * @param value string to be encoded
     * @return encoded parameters string
     */
    public static String encode(String value) {
        String encoded = null;
        try {
            encoded = URLEncoder.encode(value, "UTF-8");
        } catch (UnsupportedEncodingException ignore) 
        {
        }
        StringBuffer buf = new StringBuffer(encoded.length());
        char curChar;
        for (int i = 0; i < encoded.length(); i++) {
        	curChar = encoded.charAt(i);
            if (curChar == '*') {
                buf.append("%2A");
            } else if (curChar == '+') {
                buf.append("%20");
            } else if (curChar == '%' && (i + 1) < encoded.length()
                    && encoded.charAt(i + 1) == '7' && encoded.charAt(i + 2) == 'E') {
                buf.append('~');
                i += 2;
            } else {
                buf.append(curChar);
            }
        }
        return buf.toString();
    }
    
    
}
